package tix

import java.security.MessageDigest
import java.io.ObjectInputStream
import java.io.File
import java.io.ObjectOutputStream
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream

import collection.immutable

/** Runs a command synchronously. */
class Command(
  cmd: collection.Seq[String],
  cwd: Option[File] = None,
  env: collection.Map[String, String] = immutable.HashMap.empty,
  logPrefix: Option[String] = None) {

  /** Full command-line. */
  val args: immutable.List[String] = cmd.toList

  /** Prefix used for files/directories created during the command run. */
  val prefix = {
    if (!logPrefix.isEmpty) logPrefix.get
    else new File(args(0)).getName()
  }

  /** Time-stamp of the command. */
  val timestamp = Global.now

  /** Working directory of the command. */
  val workDir = {
    val wdir =
      if (!cwd.isEmpty) cwd.get
      else new File(Global.tmpDir, "%s-%s.workdir".format(prefix, timestamp))

    if (!wdir.exists())
      assert(wdir.mkdirs())
    assert(wdir.isDirectory())

    wdir
  }

  /** File where the command stdout is collected. */
  val stdout = new File(Global.tmpDir, "%s-%s.stdout".format(prefix, timestamp))

  /** File where the command stderr is collected. */
  val stderr = new File(Global.tmpDir, "%s-%s.stderr".format(prefix, timestamp))

  private val builder = {
    val builder = new java.lang.ProcessBuilder(cmd: _*)
      .redirectInput(new File("/dev/null"))
      .redirectOutput(stdout)
      .redirectError(stderr)
      .directory(workDir)
    env foreach { case (varname, value) => builder.environment.put(varname, value) }
    builder
  }

  val process = {
    Console.println("Running command: " + (args mkString " "))
    builder.start()
  }

  assert(process.waitFor() == 0)
}

object Serializer {
  def serialize(o: AnyRef): Array[Byte] = {
    val bos = new ByteArrayOutputStream()
    val out = new ObjectOutputStream(bos)
    out.writeObject(o)
    out.flush()
    return bos.toByteArray()
  }

  def deserialize(bytes: Array[Byte]): AnyRef = {
    val bis = new ByteArrayInputStream(bytes)
    val in = new ObjectInputStream(bis)
    return in.readObject()
  }

  def deserializeAs[T](bytes: Array[Byte]): T = {
    return deserialize(bytes).asInstanceOf[T]
  }
}

object Hasher {
  def md5() = MessageDigest.getInstance("MD5")
  def sha1() = MessageDigest.getInstance("SHA-1");
  def sha256() = MessageDigest.getInstance("SHA-256");

  def md5(any: AnyRef): String = {
    val hashBytes = md5.digest(Serializer.serialize(any))
    return hashBytes.map { "%02x" format _ } mkString ":"
  }
}
